package edu.whu.rose.clock.ssql.subgraph;

import java.util.HashSet;

import com.sleepycat.je.DatabaseException;

import edu.whu.rose.clock.ssql.util.IntLinkedList;
import edu.whu.rose.clock.ssql.util.IntLinkedNode;

public class VSILCache {

	private final int order;
	private final int capacity;
	private final SubgraphDB sgdb;

	private int[][] cache;
	private int[] cc; // cc[i] is the capacity of cache[i]
	private int[] cs; // cs[i] is the size of cache[i]

	private HashSet<Integer> stored;

	private IntLinkedList queue;
	private int size;

	private double visit = 0.0;
	private double hit = 0.0;

	private final int defualtSize = 8;

	public VSILCache(int order, int capacity, SubgraphDB sgdb) {
		this.order = order;
		this.cache = new int[order][];
		this.cc = new int[order];
		this.cs = new int[order];
		if (capacity <= 0) {
			System.exit(1);
		}
		this.capacity = capacity;
		this.queue = new IntLinkedList();
		this.stored = new HashSet<Integer>();
		this.sgdb = sgdb;
	}

	public void insert(int vertex, int subgraph) {
		int index = vertex - 1;
		Integer I = new Integer(index);
		if (cache[index] == null) {
			if (size == capacity) {
				IntLinkedNode head = queue.removeHead();
				int i = head.getIndex();
				remove(i);
			}
			if (stored.contains(I)) {
				cache[index] = getFromDB(vertex);
				cc[index] = cache[index].length;
				stored.remove(I);
				visit++;
			} else {
				cc[index] = defualtSize;
				cache[index] = new int[cc[index]];
			}
			queue.insertAtEnd(new IntLinkedNode(index));
			size++;
		} else {
			visit++;
			hit++;
		}
		if (cs[index] == cc[index]) {
			int[] temp = cache[index];
			cc[index] = 2 * cc[index];
			cache[index] = new int[cc[index]];
			for (int i = 0; i < temp.length; i++) {
				cache[index][i] = temp[i];
			}
		}
		cache[index][cs[index]] = subgraph;
		cs[index]++;
	}

	public void remove(int index) {
		int[] subgraphs = this.cache[index];
		cache[index] = null;
		putIntoDB(index + 1, subgraphs);
		stored.add(new Integer(index));
		size--;
	}

	private void putIntoDB(int vertex, int[] subgraphs) {
		try {
			int index = vertex - 1;
			int[] list = new int[cs[index]];
			for (int i = 0; i < cs[index]; i++) {
				list[i] = subgraphs[i];
			}
			sgdb.insertToInvertedList(vertex, list);
		} catch (DatabaseException ex) {
			ex.printStackTrace();
		}
	}

	private int[] getFromDB(int vertex) {
		try {
			return sgdb.getFromInvertedList(vertex);
		} catch (DatabaseException ex) {
			ex.printStackTrace();
			return null;
		}
	}

	public void flush() {
		for (int i = 0; i < order; i++) {
			if (this.cache[i] != null) {
				int[] subgraphs = cache[i];
				putIntoDB(i + 1, subgraphs);
			} else {
				if (!stored.contains(new Integer(i))) {
					System.err.println("Error: found a vertex " + (i + 1)
							+ " not belong to any subgraph");
				}
			}
		}
	}

	public void statistic() {
		System.out.println("Hit rate: " + (100 * hit / visit) + "%");
	}

}
